#include "odbc_sys.h"
#include "utils.h"
#include <WinNT.h>

static char W_EMPTY[] = {0};
//static char W_ODBC_INI[] = {'O', 'D', 'B', 'C', '.', 'I', 'N', 'I', 0};
static char W_DSN[] = {'D', 'S', 'N', 0 };
static char W_DRIVER[] = {'D', 'r', 'i', 'v', 'e', 'r', 0 };
static char W_DESCRIPTION[] = { 'D', 'E', 'S', 'C', 'R', 'I', 'P', 'T', 'I', 'O', 'N', 0 };
static char W_SERVER[] = { 'S', 'E', 'R', 'V', 'E', 'R', 0 };
static char W_UID[] = { 'U', 'I', 'D', 0 };
static char W_USER[] = {'U', 'S', 'E', 'R', 0 } ;
static char W_PWD[] = {'P', 'W', 'D', 0};
static char W_PASSWORD[] = {'P', 'A', 'S', 'S', 'W', 'O', 'R', 'D', 0 };
static char W_DB[] = {'D', 'B', 0};
static char W_DATABASE[] = {'D', 'A', 'T', 'A', 'B', 'A', 'S', 'E', 0 };
//static char W_OPTION[] = {'O', 'P', 'T', 'I', 'O', 'N', 0};
static char W_PORT[] = {'P', 'O', 'R', 'T', 0};
static char W_DELAY[] = {'D','E','L','A','Y',0};

/*
 SQLHENV, SQLHDBC, and SQLHSTMT synchronization
 is done using a critical section in ENV structure.
*/
#if defined(_WIN32) || defined(_WIN64)
SQLRETURN HDBC_LOCK(SQLHDBC hdbc)			
{						
	dbc_t *d;					

	if ((hdbc) == SQL_NULL_HDBC) 
	{		
		return SQL_INVALID_HANDLE;		
	}					

	d = (dbc_t *) (hdbc);				
	if (d->magic != DBC_MAGIC || !d->env)
	{	
		return SQL_INVALID_HANDLE;		
	}

	if (d->env->magic != ENV_MAGIC) 
	{		
		return SQL_INVALID_HANDLE;		
	}		

	EnterCriticalSection(&d->env->cs);		
	d->env->owner = GetCurrentThreadId();
	return SQL_SUCCESS;
}

void HDBC_UNLOCK(SQLHDBC hdbc)
{			
	if ((hdbc) != SQL_NULL_HDBC) 
	{		
		dbc_t *d;		
		d = (dbc_t *) (hdbc);			
		if (d->magic == DBC_MAGIC && d->env &&	d->env->magic == ENV_MAGIC) 
		{	
			d->env->owner = 0;			
			LeaveCriticalSection(&d->env->cs);	
		}					
	}
}

SQLRETURN HSTMT_LOCK(SQLHSTMT hstmt)			
{						
	dbc_t *d;					

	if ((hstmt) == SQL_NULL_HSTMT)
	{		
		return SQL_INVALID_HANDLE;		
	}	

	d = (dbc_t *) ((stmt_t *) (hstmt))->dbc;	
	if (d->magic != DBC_MAGIC || !d->env)
	{	
		return SQL_INVALID_HANDLE;		
	}	

	if (d->env->magic != ENV_MAGIC)
	{		
		return SQL_INVALID_HANDLE;		
	}	

	EnterCriticalSection(&d->env->cs);
	d->env->owner = GetCurrentThreadId();
	return SQL_SUCCESS;
}

void HSTMT_UNLOCK(SQLHSTMT hstmt)
{			
	if ((hstmt) != SQL_NULL_HSTMT) 
	{		
		dbc_t *d;					
		d = (dbc_t *) ((stmt_t *) (hstmt))->dbc;	
		if (d->magic == DBC_MAGIC && d->env && d->env->magic == ENV_MAGIC)
		{	
			d->env->owner = 0;			
			LeaveCriticalSection(&d->env->cs);	
		}					
	}
}
#endif

data_src_t *ds_new()
{
	data_src_t *ds = (data_src_t *)malloc(sizeof(data_src_t));
	if (!ds) {
		return NULL;
	}

	memset(ds, 0, sizeof(data_src_t));

	ds->port = 8200;
	ds->timeout = 30;

	return ds;
}

void ds_delete(data_src_t *ds)
{
	free(ds->name);
	free(ds->driver);
	free(ds->description);
	free(ds->server);
	free(ds->uid);
	free(ds->pwd);
	free(ds->database);
	free(ds->initstmt);
	free(ds);
}

size_t sqlwcharlen(const char *wstr)
{
	size_t len= 0;
	while (wstr && *wstr++)
		len++;
 	return len;
}

size_t sqlcharlen(const SQLCHAR *str)
{
	size_t len = 0;
	while (str && *str++)
		len++;
	return len;
}

char *sqlwchardup(const char *wstr, size_t charlen)
{
	size_t chars= charlen == SQL_NTS ? sqlwcharlen(wstr) : charlen;
	char *res= (char *)malloc((chars + 1) * sizeof(char));
	if (!res)
		return NULL;
	memcpy(res, wstr, chars * sizeof(char));
	res[chars] = 0;
	return res;
}

SQLCHAR *sqlchardup(const SQLCHAR *str, size_t charlen)
{
	size_t chars = charlen == SQL_NTS ? sqlcharlen(str) : charlen;
	SQLCHAR *res = (SQLCHAR *)malloc((chars + 1) *sizeof(SQLCHAR));
	if (!res)
		return NULL;
	memcpy(res, str, chars *sizeof(SQLCHAR));
	res[chars] =0;
	return res;
}

/*
* Convert a char string to a long integer.
*
* @return The integer result of the conversion or 0 if the
*         string could not be parsed.
*/
unsigned long sqlwchartoul(const char *wstr, const char **endptr)
{
	unsigned long res= 0;
	char c;

	if (!wstr)
		return 0;

	while (c= *wstr)
	{
		if (c < '0' || c > '9')
			break;
		res*= 10;
		res+= c - '0';
		wstr++;
	}

	if (endptr)
		*endptr= wstr;

	return res;
}

unsigned long sqlchartoul(const SQLCHAR *str, const SQLCHAR **endptr)
{
	unsigned long res = 0;
	SQLCHAR c;

	if (!str)
		return 0;

	while (c =*str) {
		if (c < '0' || c > '9')
			break;
		res *=10;
		res +=c -'0';
		str++;
	}

	if (endptr)
		*endptr = str;

	return res;
}

/*
* Compare two char strings ignoring case. This is only
* case-insensitive over the ASCII range of characters.
*
* @return 0 if the strings are the same, 1 if they are not.
*/
int sqlwcharcasecmp(const char *s1, const char *s2)
{
	char c1, c2;
	while (*s1 && *s2)
	{
		c1= *s1;
		c2= *s2;
		/* capitalize both strings */
		if (c1 >= 'a')
			c1 -= ('a' - 'A');
		if (c2 >= 'a')
			c2 -= ('a' - 'A');
		if (c1 != c2)
			return 1;
		s1++;
		s2++;
	}

	/* one will be null, so both must be */
	return *s1 != *s2;
}

/*
* Set a string attribute of a given data source object. The string
* will be copied into the object.
*/
int ds_set_strattr(char **attr, const char *val)
{
	free(*attr);
	if (val && *val)
		*attr= sqlwchardup(val, SQL_NTS);
	else
		*attr= NULL;
	return *attr || 0;
}

/*
* Same as ds_set_strattr, but allows truncating the given string. If
* charcount is 0 or SQL_NTS, it will act the same as ds_set_strattr.
*/
int ds_set_strnattr(char **attr, const char *val, size_t charcount)
{
	free(*attr);

	if (charcount == SQL_NTS)
		charcount= sqlwcharlen(val);


	if (!charcount)
	{
		*attr= NULL;
		return 1;
	}

	if (val && *val)
		*attr= sqlwchardup(val, charcount);
		

	else
		*attr= NULL;
	return *attr || 0;
}

/*
* Convenience function to get the current config mode.
*/
UWORD config_get()
{
	UWORD mode;
	SQLGetConfigMode(&mode);
	return mode;
}

/*
* Convenience function to set the current config mode. Returns the
* mode in use when the function was called.
*/
UWORD config_set(UWORD mode)
{
	UWORD current= config_get();
	SQLSetConfigMode(mode);
	return current;
}

/*
* Internal function to map a parameter name of the data source object
* to the pointer needed to set the parameter. Only one of strdest or
* intdest will be set. strdest and intdest will be set for populating
* string (char *) or int parameters.
*/
void ds_map_param(data_src_t *ds, const char *param,
				  char ***strdest, unsigned int **intdest)
{
	*strdest= NULL;
	*intdest= NULL;
	/* parameter aliases can be used here, see W_UID, W_USER */
	if (!sqlwcharcasecmp(W_DSN, param))
		*strdest= &(char *)ds->name;
	else if (!sqlwcharcasecmp(W_DRIVER, param))
		*strdest= &(char *)ds->driver;
	else if (!sqlwcharcasecmp(W_DESCRIPTION, param))
		*strdest= &(char *)ds->description;
	else if (!sqlwcharcasecmp(W_SERVER, param))
		*strdest= &(char *)ds->server;
	else if (!sqlwcharcasecmp(W_UID, param))
		*strdest= &(char *)ds->uid;
	else if (!sqlwcharcasecmp(W_USER, param))
		*strdest= &(char *)ds->uid;
	else if (!sqlwcharcasecmp(W_PWD, param))
		*strdest= &(char *)ds->pwd;
	else if (!sqlwcharcasecmp(W_PASSWORD, param))
		*strdest= &(char *)ds->pwd;
	else if (!sqlwcharcasecmp(W_DB, param))
		*strdest= &(char *)ds->database;
	else if (!sqlwcharcasecmp(W_DATABASE, param))
		*strdest= &(char *)ds->database;
	//else if (!sqlwcharcasecmp(W_OPTION, param))
	//	*strdest= &ds->option;
	else if (!sqlwcharcasecmp(W_PORT, param))
		*intdest= &ds->port;
	else if (!sqlwcharcasecmp(W_DELAY, param))
		*intdest = &ds->timeout;
	/* DS_PARAM */
}

/*
* Lookup a data source in the system. The name will be read from
* the object and the rest of the details will be populated.
*
* If greater-than zero is returned, additional information
* can be obtained from SQLInstallerError(). A less-than zero return code
* indicates that the driver could not be found.
*/
int ds_lookup(data_src_t *ds)
{
	char buf[8192];
	char *entries= buf;
	char **dest;
	char val[256];
	int size, used;
	int rc= 0;
	UWORD config_mode= config_get();
	unsigned int *intdest;
	/* No need for SAVE_MODE() because we always call config_get() above. */

#ifdef _WIN32
	/* We must do this to detect the WinXP bug mentioned below */
	memset(buf, 0xff, sizeof(buf));
#endif

	/* get entries and check if data source exists */

	size= SQLGetPrivateProfileString(
		(LPCSTR)ds->name/*"op_dsn"*/, 
		NULL, 
		(LPCSTR)W_EMPTY,
		(LPSTR)buf, 
		8192, 
		"ODBC.INI");
	if (size < 1)
	{
		rc= -1;
		goto end;
	}

	RESTORE_MODE();

#ifdef _WIN32
	/*
	* In Windows XP, there is a bug in SQLGetPrivateProfileString
	* when mode is ODBC_BOTH_DSN and we are looking for a system
	* DSN. In this case SQLGetPrivateProfileString will find the
	* system dsn but return a corrupt list of attributes. 
	*
	* See debug code above to print the exact data returned.
	* See also: http://support.microsoft.com/kb/909122/
	*/
	if (config_mode == ODBC_BOTH_DSN &&
		/* two null chars or a null and some bogus character */
		*(entries + sqlwcharlen(entries)) == 0 &&
		(*(entries + sqlwcharlen(entries) + 1) > 0x7f ||
		*(entries + sqlwcharlen(entries) + 1) == 0))
	{
		/* revert to system mode and try again */
		config_set(ODBC_SYSTEM_DSN);
		if ((size= SQLGetPrivateProfileString((LPCSTR)ds->name, NULL, (LPCSTR)W_EMPTY,
			(LPSTR)buf, 8192, "ODBC.INI")) < 1)
		{
			rc= -1;
			goto end;
		}
	}
#endif

	for (used= 0; used < size; used += sqlwcharlen(entries) + 1,
		entries += sqlwcharlen(entries) + 1)
	{
		int valsize;
		ds_map_param(ds, entries, &dest, &intdest);

		if ((dest || intdest) &&
			(valsize= SQLGetPrivateProfileString((LPCSTR)ds->name, (LPCSTR)entries, (LPCSTR)W_EMPTY,
			(LPSTR)val, 256, "ODBC.INI")) < 0)
		{
			rc= 1;
			goto end;
		}
		else if (!valsize)
			/* skip blanks */;
		else if (dest && !*dest)
			ds_set_strnattr(dest, val, valsize);
		else if (intdest)
			*intdest = sqlwchartoul(val, NULL);

		RESTORE_MODE();
	}

end:
	config_set(config_mode);
	return rc;
}

/*
Duplicate string using malloc().
@param str string to be duplicated
@result pointer to new string or NULL
*/
char * strdup_(const char * str)
{
	char *p = NULL;
	if (str)
	{
		p = (char *)malloc(strlen(str) + 1);
		if (p) 
		{
			strcpy(p, str);
		}
	}
	return p;
}

/*
Return length of UNICODE string.
@param str UNICODE string
@result length of string
*/
int uc_strlen(char *str)
{
	int len = 0;
	if (str) 
	{
		while (*str)
		{
			++len;
			++str;
		}
	}
	return len;
}

/*
Copy UNICODE string like strncpy().
@param dest destination area
@param src source area
@param len length of source area
@return pointer to destination area
*/
char *uc_strncpy(char *dest, char *src, int len)
{
	int i = 0;

	while (i < len)
	{
		if (!src[i]) 
		{
			break;
		}

		dest[i] = src[i];
		++i;
	}

	if (i < len) 
	{
		dest[i] = 0;
	}

	return dest;
}

/*
Make UNICODE string from UTF8 string into buffer.
@param str UTF8 string to be converted
@param uc destination area to receive UNICODE string
@param ucLen byte length of destination area
*/
void uc_from_utf_buf(unsigned char *str, char *uc, int ucLen)
{
	ucLen = ucLen / sizeof (char);

	if (!uc || ucLen < 0)
		return;

	uc[0] = 0;
	if (str) 
	{
		int i = 0;

		while (*str && i < ucLen)
		{
			unsigned char c = str[0];

			if (c < 0xc0)	/* < 12	*/
			{
				uc[i++] = c;
				++str;
			}
			else if (c < 0xe0)	/* < 14	*/
			{
				if ((str[1] & 0xc0) == 0x80) 
				{
					unsigned long t = ((c & 0x1f) << 6) | (str[1] & 0x3f);
					uc[i++] = t;
					str += 2;
				}
				else
				{
					uc[i++] = c;
					++str;
				}
			}
			else if (c < 0xf0)	/* < 15	 */
			{
				if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80) 
				{
					unsigned long t = ((c & 0x0f) << 12) | ((str[1] & 0x3f) << 6) | (str[2] & 0x3f);
					uc[i++] = t;
					str += 3;
				} 
				else
				{
					uc[i++] = c;
					++str;
				}
			}
			else if (c < 0xf8)
			{
				if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 &&
					(str[3] & 0xc0) == 0x80)
				{
					unsigned long t = ((c & 0x03) << 18) |
						((str[1] & 0x3f) << 12) |
						((str[2] & 0x3f) << 6) |
						(str[4] & 0x3f);
					if (sizeof (char) == 2 * sizeof (char) &&	t >= 0x10000)						
					{
						t -= 0x10000;
						uc[i++] = 0xd800 | (t & 0x3ff);

						if (i > ucLen) 
						{
							break;
						}

						t = 0xdc00 | ((t >> 10) & 0x3ff);
					}

					uc[i++] = t;
					str += 4;
				}
				else
				{
					uc[i++] = c;
					++str;
				}				 
			}
			else if (c < 0xfc)
			{
				if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 &&
					(str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80) 
				{
					unsigned long t = ((c & 0x01) << 24) |
						((str[1] & 0x3f) << 18) | ((str[2] & 0x3f) << 12) |
						((str[4] & 0x3f) << 6) | (str[5] & 0x3f);

					if (sizeof (char) == 2 * sizeof (char) && t >= 0x10000)
					{
						t -= 0x10000;
						uc[i++] = 0xd800 | (t & 0x3ff);
						if (i >= ucLen) 
						{
							break;
						}
						t = 0xdc00 | ((t >> 10) & 0x3ff);
					}
					uc[i++] = t;
					str += 5;
				} 
				else 
				{
					uc[i++] = c;
					++str;
				}
			}
			else
			{
				/* ignore */
				++str;
			}
		}/* end of while */

		if (i < ucLen) 
		{
			uc[i] = 0;
		}
	}
}

/*
Make UNICODE string from UTF8 string.
@param str UTF8 string to be converted
@param len length of UTF8 string
@return alloc'ed UNICODE string to be free'd by uc_free()
*/
char * uc_from_utf(unsigned char *str, int len)
{
	char *uc = NULL;

	if (str) 
	{
		if (len == SQL_NTS) 
		{
			len = strlen((char *) str);
		}
		len = sizeof (char) * (len + 1);
		uc = (char *)malloc(len);
		if (uc) 
		{
			uc_from_utf_buf(str, uc, len);
		}
	}

	return uc;
}

/*
Make UTF8 string from UNICODE string.
@param str UNICODE string to be converted
@param len length of UNICODE string in bytes
@return alloc'ed UTF8 string to be free'd by uc_free()
*/
char * uc_to_utf(char *str, int len)
{
	int i;
	char *cp, *ret = NULL;

	if (!str) 
	{
		return ret;
	}

	if (len == SQL_NTS) 
	{
		len = uc_strlen(str);
	}
	else 
	{
		len = len / sizeof (char);
	}

	cp = (char *)malloc(len * 6 + 1);
	if (!cp) 
	{
		return ret;
	}

	ret = cp;
	for (i = 0; i < len; i++) 
	{
		unsigned long c = str[i];

		if (sizeof (char) == 2 * sizeof (char)) 
		{
			c &= 0xffff;
		}

		if (c < 0xc0) 
		{
			*cp++ = c;
		} 
		else if (c < 0x800) 
		{
			*cp++ = 0xc0 | ((c >> 6) & 0x1f);
			*cp++ = 0x80 | (c & 0x3f);
		} 
		else if (c < 0x10000) 
		{
			if (sizeof (char) == 2 * sizeof (char) &&
				c >= 0xd800 && c <= 0xdbff && i + 1 < len) 
			{
				unsigned long c2 = str[i + 1] & 0xffff;

				if (c2 >= 0xdc00 && c <= 0xdfff) 
				{
					c = ((c & 0x3ff) | ((c2 & 0x3ff) << 10)) + 0x10000;

					*cp++ = 0xf0 | ((c >> 18) & 0x07);
					*cp++ = 0x80 | ((c >> 12) & 0x3f);
					*cp++ = 0x80 | ((c >> 6) & 0x3f);
					*cp++ = 0x80 | (c & 0x3f);

					++i;

					continue;
				}
			}

			*cp++ = 0xe0 | ((c >> 12) & 0x0f);
			*cp++ = 0x80 | ((c >> 6) & 0x3f);
			*cp++ = 0x80 | (c & 0x3f);
		} 
		else if (c < 0x200000) 
		{
			*cp++ = 0xf0 | ((c >> 18) & 0x07);
			*cp++ = 0x80 | ((c >> 12) & 0x3f);
			*cp++ = 0x80 | ((c >> 6) & 0x3f);
			*cp++ = 0x80 | (c & 0x3f);
		} 
		else if (c < 0x4000000) 
		{
			*cp++ = 0xf8 | ((c >> 24) & 0x03);
			*cp++ = 0x80 | ((c >> 18) & 0x3f);
			*cp++ = 0x80 | ((c >> 12) & 0x3f);
			*cp++ = 0x80 | ((c >> 6) & 0x3f);
			*cp++ = 0x80 | (c & 0x3f);
		} else if (c < 0x80000000) 
		{
			*cp++ = 0xfc | ((c >> 31) & 0x01);
			*cp++ = 0x80 | ((c >> 24) & 0x3f);
			*cp++ = 0x80 | ((c >> 18) & 0x3f);
			*cp++ = 0x80 | ((c >> 12) & 0x3f);
			*cp++ = 0x80 | ((c >> 6) & 0x3f);
			*cp++ = 0x80 | (c & 0x3f);
		}
	}

	*cp = '\0';
	return ret;
}

/*
Make UTF8 string from UNICODE string.
@param str UNICODE string to be converted
@param len length of UNICODE string in characters
@return alloc'ed UTF8 string to be free'd by uc_free()
*/
char * uc_to_utf_c(char *str, int len)
{
	if (len != SQL_NTS) 
	{
		len = len * sizeof (char);
	}

	return uc_to_utf(str, len);
}

/*
* Free memory given pointer to memory pointer
** @param x pointer to pointer to memory to be free'd
*/
void freep(void *x)
{
	if (x && ((char **)x)[0])
	{
		free(((char**) x)[0]);
		((char **) x)[0] = NULL;
	}
}

void set_stat_dbc(dbc_t *d, int native_error, char *msg, char *st, ...)
{
	va_list ap;

	if (!d) 
	{
		return;
	}

	d->native_error = native_error;
	d->log_msg[0] = '\0';

	if (msg) 
	{
		int count;

		va_start(ap, st);
		count = vsnprintf((char *) d->log_msg, sizeof (d->log_msg), msg, ap);
		va_end(ap);
		if (count < 0) {
			d->log_msg[sizeof (d->log_msg) - 1] = '\0';
		}
	}

	if (!st) 
	{
		st = "?????";
	}

	strncpy(d->sql_state, st, 5);
	d->sql_state[5] = '\0';
}

void set_stat_stmt(stmt_t *s, int native_error, char *msg, char *st, ...)
{
	va_list ap;

	if (!s)
	{
		return;
	}

	s->native_error = native_error;		/* Native error	*/
	s->log_msg[0] = '\0';

	if (msg)
	{
		int count;

		va_start(ap, st);
		count = vsnprintf((char *) s->log_msg, sizeof(s->log_msg), msg, ap);
		va_end(ap);
		if(count < 0)
		{
			s->log_msg[sizeof(s->log_msg) - 1] = '\0'; 		
		}
	}

	if (!st) 
	{
		st = "?????";
	}

	strncpy(s->sql_state, st, 5);
	s->sql_state[5] = '\0';
}


/*
Report S1000 (out of memory) SQL error given stmt_t
@param s statment pointer
@result ODBC error code
*/
SQLRETURN no_memory(stmt_t *s)
{
	set_stat_stmt(s, -1, "out of memory", "S1000");
	return SQL_ERROR;
}

/*
Report S1000 (not connected) SQL error given stmt_t.
@param s statement pointer
@result ODBC error code
*/
SQLRETURN no_connect(stmt_t *s)
{
	set_stat_stmt(s, -1, "not connected", "HY000");
	return SQL_ERROR;
}

/*
Setup parameter buffer for deferred parameter.
@param s pointer to stmt_t
@param p pointer to bind_param_t
@result ODBC error code (success indicated by SQL_NEED_DATA)
*/
SQLRETURN setup_param_buf(stmt_t *s, bind_param_t *p)
{
	return SQL_NEED_DATA;
}

/*
Free statement's result.
@param s statement pointer
@param clrcols flag to clear column information

The result rows are free'd using the rowfree function pointer.
If clrcols is greater than zero, then column bindings and dynamic column
descriptions are free'd.
If clrcols is less than zero, then dynamic column descriptions are free'd.
*/
void free_result(stmt_t *s, int clrcols)
{
	freep(&s->bincache);
	s->bincell = NULL;
	s->binlen = 0;

	s->nrows = -1;
	if (clrcols > 0) 
	{
		freep(&s->bindcols);
		s->nbindcols = 0;
	}

	if (clrcols) 
	{
		s->cols = NULL;
		s->ncols = 0;
		s->no_wchar[1] = 0;
	}
}

int map_default_type(int type, int stype, int nosign, int no_wchar)
{
	if (type == SQL_C_DEFAULT) 
	{
		switch (stype) 
		{
		case SQL_INTEGER:
			type = (nosign > 0) ? SQL_C_ULONG : SQL_C_LONG;
			break;
		case SQL_TINYINT:
			type = (nosign > 0) ? SQL_C_UTINYINT : SQL_C_TINYINT;
			break;
		case SQL_SMALLINT:
			type = (nosign > 0) ? SQL_C_USHORT : SQL_C_SHORT;
			break;
		case SQL_FLOAT:
			type = SQL_C_FLOAT;
			break;
		case SQL_DOUBLE:
			type = SQL_C_DOUBLE;
			break;
		case SQL_TIMESTAMP:
			type = SQL_C_TIMESTAMP;
			break;
		case SQL_TIME:
			type = SQL_C_TIME;
			break;
		case SQL_DATE:
			type = SQL_C_DATE;
			break;
#ifdef SQL_C_TYPE_TIMESTAMP
		case SQL_TYPE_TIMESTAMP:
			type = SQL_C_TYPE_TIMESTAMP;
			break;
#endif
#ifdef SQL_C_TYPE_TIME
		case SQL_TYPE_TIME:
			type = SQL_C_TYPE_TIME;
			break;
#endif
#ifdef SQL_C_TYPE_DATE
		case SQL_TYPE_DATE:
			type = SQL_C_TYPE_DATE;
			break;
#endif
#ifdef WINTERFACE
		case SQL_WVARCHAR:
		case SQL_WCHAR:
#ifdef SQL_WLONGVARCHAR
		case SQL_WLONGVARCHAR:
#endif
			type = no_wchar ? SQL_C_CHAR : SQL_C_WCHAR;
			break;
#endif
		case SQL_BINARY:
		case SQL_VARBINARY:
		case SQL_LONGVARBINARY:
			type = SQL_C_BINARY;
			break;
#ifdef SQL_BIT
		case SQL_BIT:
			type = SQL_C_BIT;
			break;
#endif
#ifdef SQL_BIGINT
		case SQL_BIGINT:
			type = (nosign > 0) ? SQL_C_UBIGINT : SQL_C_SBIGINT;
			break;
#endif
		default:
#ifdef WINTERFACE
			type = no_wchar ? SQL_C_CHAR : SQL_C_WCHAR;
#else
			type = SQL_C_CHAR;
#endif
			break;
		}
	}
	return type;
}

/*
Get boolean flag from string.
@param string string to be inspected
@result true or false
*/
int get_bool(char *string)
{
	if (string) 
	{
		return string[0] && strchr("Yy123456789Tt", string[0]) != NULL;
	}
	return 0;
}

/*
Internal locale neutral strtod function.
@param data pointer to string
@param endp pointer for ending character
@result double value
*/
double ln_strtod(const char *data, char **endp)
{
	return 0.0;
}

/*
<Note> need code
Internal free function for HSTMT.
@param stmt statement handle
@result ODBC error code
*/
SQLRETURN free_stmt(SQLHSTMT stmt)
{
	return SQL_SUCCESS;
}


/*
Clear out parameter bindings, if any.
@param s statement pointer
*/
SQLRETURN free_params(stmt_t *s)
{
	return SQL_SUCCESS;
}

/*
Return number of month days.
@param year
@param month 1..12
@result number of month days or 0
*/
int get_month_days(int year, int month)
{
	static const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
	int mday;

	if (month < 1) 
	{
		return 0;
	}
	
	mday = mdays[(month - 1) % 12];

	if (mday == 28 && year % 4 == 0 && (!(year % 100 == 0) || year % 400 == 0)) 
	{
		mday++;
	}

	return mday;
}

/*
Convert string to ODBC TIMESTAMP_STRUCT.
@param str string to be converted
@param tss output TIMESTAMP_STRUCT
@result 0 on success, -1 on error

Strings of the format 'YYYYMMDDhhmmssff' or 'YYYY-MM-DD hh:mm:ss ff'
or 'YYYY/MM/DD hh:mm:ss ff' or 'hh:mm:ss ff YYYY-MM-DD' are
converted to a TIMESTAMP_STRUCT. The ISO8601 formats
YYYY-MM-DDThh:mm:ss[.f]Z
YYYY-MM-DDThh:mm:ss[.f]shh:mm
are also supported. In case a time zone field is present,
the resulting TIMESTAMP_STRUCT is expressed in UTC.
*/
int str2timestamp(char *str, TIMESTAMP_STRUCT *tss)
{
	int i, m, n, err = 0;
	char *p, *q, in = '\0';

	tss->year = tss->month = tss->day = 0;
	tss->hour = tss->minute = tss->second = 0;
	tss->fraction = 0;

	p = str;
	while (*p && !ISDIGIT(*p)) 
	{
		++p;
	}

	q = p;
	i = 0;
	while (*q && ISDIGIT(*q)) 
	{
		++i;
		++q;
	}

	if (i >= 14) 
	{
		char buf[16];

		strncpy(buf, p + 0, 4); buf[4] = '\0';
		tss->year = strtol(buf, NULL, 10);
		strncpy(buf, p + 4, 2); buf[2] = '\0';
		tss->month = strtol(buf, NULL, 10);
		strncpy(buf, p + 6, 2); buf[2] = '\0';
		tss->day = strtol(buf, NULL, 10);
		strncpy(buf, p + 8, 2); buf[2] = '\0';
		tss->hour = strtol(buf, NULL, 10);
		strncpy(buf, p + 10, 2); buf[2] = '\0';
		tss->minute = strtol(buf, NULL, 10);
		strncpy(buf, p + 12, 2); buf[2] = '\0';
		tss->second = strtol(buf, NULL, 10);
		if (i > 14) 
		{
			m = i - 14;
			strncpy(buf, p + 14, m);
			while (m < 9) 
			{
				buf[m] = '0';
				++m;
			}
			buf[m] = '\0';
			tss->fraction = strtol(buf, NULL, 0);
		}
		m = 7;
		goto done;
	}
	m = i = 0;
	while ((m & 7) != 7) 
	{
		q = NULL; 
		n = strtol(p, &q, 10);
		if (!q || q == p) 
		{
			if (*q == '\0') 
			{
				if (m < 1) 
				{
					err = 1;
				}
				goto done;
			}
		}

		if (in == '\0') 
		{
			switch (*q) 
			{
			case '-':
			case '/':
				if ((m & 1) == 0) 
				{
					in = *q;
					i = 0;
				}
				break;
			case ':':
				if ((m & 2) == 0) 
				{
					in = *q;
					i = 0;
				}
				break;
			case ' ':
			case '.':
				break;
			default:
				in = '\0';
				i = 0;
				break;
			}
		}
		
		switch (in) 
		{
		case '-':
		case '/':
			switch (i) 
			{
			case 0: tss->year = n; break;
			case 1: tss->month = n; break;
			case 2: tss->day = n; break;
			}
			
			if (++i >= 3)
			{
				i = 0;
				m |= 1;
				if (!(m & 2)) 
				{
					m |= 8;
				}
			
			goto skip;
			} 
			else 
			{
				++q;
			}		
			break;
		case ':':
			switch (i)
			{
			case 0: tss->hour = n; break;
			case 1: tss->minute = n; break;
			case 2: tss->second = n; break;
			}
		
			if (++i >= 3) 
			{
				i = 0;
				m |= 2;
				
				if (*q == '.') 
				{
					in = '.';
					goto skip2;
				}
				
				if (*q == ' ') 
				{
					if ((m & 1) == 0) 
					{
						char *e = NULL;
						int dummy;

						dummy = strtol(q + 1, &e, 10);
						if (e && *e == '-') 
						{
							goto skip;
						}
					}
					
					in = '.';
					goto skip2;
				}
				goto skip;
			} 
			else 
			{
				++q;
			}
			break;
		case '.':
			if (++i >= 1) 
			{
				int ndig = q - p;

				if (p[0] == '+' || p[0] == '-') 
				{
					ndig--;
				}
				
				while (ndig < 9) 
				{
					n = n * 10;
					++ndig;
				}
				tss->fraction = n;
				m |= 4;
				i = 0;
			}
		default:
skip:
			in = '\0';
skip2:
			while (*q && !ISDIGIT(*q)) 
			{
				++q;
			}
		}//switch (in) 
		
		p = q;
	}//while ((m & 7) != 7) 
	
	if ((m & 7) > 1 && (m & 8)) 
	{
		/* ISO8601 timezone */
		if (p > str && ISDIGIT(*p)) 
		{
			int nn, sign;

			q = p - 1;
			if (*q != '+' && *q != '-') 
			{
				goto done;
			}
			
			sign = (*q == '+') ? -1 : 1;
			q = NULL;
			n = strtol(p, &q, 10);
			if (!q || *q++ != ':' || !ISDIGIT(*q)) 
			{
				goto done;
			}
			
			p = q;
			q = NULL;
			nn = strtol(p, &q, 0);

			tss->minute += nn * sign;
			if ((SQLSMALLINT) tss->minute < 0) 
			{
				tss->hour -= 1;
				tss->minute += 60;
			} 
			else if (tss->minute >= 60) 
			{
				tss->hour += 1;
				tss->minute -= 60;
			}
			
			tss->hour += n * sign;
			if ((SQLSMALLINT) tss->hour < 0) 
			{
				tss->day -= 1;
				tss->hour += 24;
			} 
			else if (tss->hour >= 24) 
			{
				tss->day += 1;
				tss->hour -= 24;
			}
			
			if ((short) tss->day < 1 || tss->day >= 28) 
			{
				int mday, pday, pmon;

				mday = get_month_days(tss->year, tss->month);
				pmon = tss->month - 1;
				if (pmon < 1) 
				{
					pmon = 12;
				}
				
				pday = get_month_days(tss->year, pmon);
				if ((SQLSMALLINT) tss->day < 1) 
				{
					tss->month -= 1;
					tss->day = pday;
				} 
				else if (tss->day > mday) 
				{
					tss->month += 1;
					tss->day = 1;
				}
				
				if ((SQLSMALLINT) tss->month < 1) 
				{
					tss->year -= 1;
					tss->month = 12;
				} 
				else if (tss->month > 12) 
				{
					tss->year += 1;
					tss->month = 1;
				}
			}
		}
	}
done:
	/* final check for overflow */
	if (err ||
		tss->month < 1 || tss->month > 12 ||
		tss->day < 1 || tss->day > get_month_days(tss->year, tss->month) ||
		tss->hour > 23 || tss->minute > 60 || tss->second > 60) 
	{
			return -1;
	}
	
	return ((m & 7) < 1) ? -1 : 0;
}


/*
 Convert string to ODBC TIME_STRUCT.
 @param str string to be converted
 @param ts output TIME_STRUCT
 @result 0 on success, -1 on error

 Strings of the format 'HHMMSS' or 'HH:MM:SS'
 are converted to a TIME_STRUCT.
*/

int str2time(char *str, TIME_STRUCT *ts)
{
	int i, err = 0;
	char *p, *q;

	ts->hour = ts->minute = ts->second = 0;
	p = str;
	while (*p && !ISDIGIT(*p)) 
	{
		++p;
	}
	
	q = p;
	i = 0;
	while (*q && ISDIGIT(*q)) 
	{
		++i;
		++q;
	}
	
	if (i >= 6) 
	{
		char buf[4];

		strncpy(buf, p + 0, 2); buf[2] = '\0';
		ts->hour = strtol(buf, NULL, 10);
		strncpy(buf, p + 2, 2); buf[2] = '\0';
		ts->minute = strtol(buf, NULL, 10);
		strncpy(buf, p + 4, 2); buf[2] = '\0';
		ts->second = strtol(buf, NULL, 10);
		goto done;
	}
	
	i = 0;
	while (i < 3) 
	{
		int n;

		q = NULL; 
		n = strtol(p, &q, 10);
		if (!q || q == p) 
		{
			if (*q == '\0') 
			{
				if (i == 0) 
				{
					err = 1;
				}
				goto done;			
			}
		}
		
		if (*q == ':' || *q == '\0' || i == 2) 
		{
			switch (i) 
			{
			case 0: ts->hour = n; break;
			case 1: ts->minute = n; break;
			case 2: ts->second = n; break;
			}
			
			++i;
			if (*q) 
			{
				++q;
			}
		} 
		else 
		{
			i = 0;
			while (*q && !ISDIGIT(*q)) {
				++q;
			}
		}
		p = q;
	}
	
done:
	/* final check for overflow */
	if (err || ts->hour > 23 || ts->minute > 59 || ts->second > 59) 
	{
		return -1;
	}
	
	return 0;
}

/*
 Convert string to ODBC DATE_STRUCT.
 @param str string to be converted
 @param ds output DATE_STRUCT
 @result 0 on success, -1 on error

 Strings of the format 'YYYYMMDD' or 'YYYY-MM-DD' or
 'YYYY/MM/DD' are converted to a DATE_STRUCT.
*/
int str2date(char *str, DATE_STRUCT *ds)
{
	int i, err = 0;
	char *p, *q;

	ds->year = ds->month = ds->day = 0;
	p = str;
	while (*p && !ISDIGIT(*p)) 
	{
		++p;
	}
	
	q = p;
	i = 0;
	while (*q && !ISDIGIT(*q)) 
	{
		++i;
		++q;
	}
	
	if (i >= 8) 
	{
		char buf[8];

		strncpy(buf, p + 0, 4); buf[4] = '\0';
		ds->year = strtol(buf, NULL, 10);
		strncpy(buf, p + 4, 2); buf[2] = '\0';
		ds->month = strtol(buf, NULL, 10);
		strncpy(buf, p + 6, 2); buf[2] = '\0';
		ds->day = strtol(buf, NULL, 10);
		goto done;
	}
	
	i = 0;
	while (i < 3) 
	{
		int n;

		q = NULL; 
		n = strtol(p, &q, 10);
		if (!q || q == p) 
		{
			if (*q == '\0') 
			{
				if (i == 0) 
				{
					err = 1;
				}
				goto done;
			}
		}
		
		if (*q == '-' || *q == '/' || *q == '\0' || i == 2) 
		{
			switch (i) 
			{
			case 0: ds->year = n; break;
			case 1: ds->month = n; break;
			case 2: ds->day = n; break;
			}
			++i;
			if (*q) {
				++q;
			}
		}
		else 
		{
			i = 0;
			while (*q && !ISDIGIT(*q)) 
			{
				++q;
			}
		}
		p = q;
	}
	
done:
	/* final check for overflow */
	if (err ||
		ds->month < 1 || ds->month > 12 ||
		ds->day < 1 || ds->day > get_month_days(ds->year, ds->month)) 
		{
			return -1;
	}
	return 0;
}

/*
 Conditionally stop running openPlant statement
 @param s statement pointer
*/
void stmt_end_if(stmt_t *s)
{
	;
}

SQLPOINTER  strmak(char *dst, const char* src, int max, SQLSMALLINT *lenp) 
{
	int len = strlen(src);
	int cnt = min(len + 1, max);
	strncpy(dst, src, cnt); 
	*lenp = (cnt > len) ? len : cnt; 
	return (SQLPOINTER) dst;
}